package com.mobi.Survey.video;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.net.Uri;

import com.mobi.Survey.Messages;

/**
 * 
 * <p>
 * <b>Author:</b> Yahya Arshad
 * <p>
 * <b>Company:</b> Mobi Media Soft
 * <p>
 * <b> Website:</b> www.mobimediasoft.com
 * <p>
 * YouTubeUtility.java
 */

public class YouTubeUtility
{

	static final String YOUTUBE_VIDEO_INFORMATION_URL = "http://www.youtube.com/get_video_info?&video_id=";

	/**
	 * 
	 * @param yId
	 * @return
	 * @throws ParserConfigurationException
	 * @throws IOException
	 * @throws SAXException
	 */
	public static String getRTSPUrl(String yId)
			throws ParserConfigurationException, SAXException, IOException
	{

		String gdata = "http://gdata.youtube.com/feeds/api/videos/";
		DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()
				.newDocumentBuilder();

		URL url = new URL(gdata + yId);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		Document doc = documentBuilder.parse(connection.getInputStream());
		Element el = doc.getDocumentElement();
		NodeList list = el.getElementsByTagName("media:content");// /media:content
		String cursor = null;
		for (int i = 0; i < list.getLength(); i++)
		{
			Node node = list.item(i);
			if (node != null)
			{
				NamedNodeMap nodeMap = node.getAttributes();
				HashMap<String, String> maps = new HashMap<String, String>();
				for (int j = 0; j < nodeMap.getLength(); j++)
				{
					Attr att = (Attr) nodeMap.item(j);
					maps.put(att.getName(), att.getValue());
				}
				if (maps.containsKey("yt:format"))
				{
					String f = maps.get("yt:format");
					if (maps.containsKey("url"))
					{
						cursor = maps.get("url");
					}
					if (f.equals("1"))
						return cursor;
				}
			}
		}

		if (cursor == null)
		{
			throw new NullPointerException();
		}
		return cursor;

	}

	/**
	 * Youtube Videos with Format 18 will be downloaded because android devices
	 * below 3.0 can not play other video format well
	 * 
	 * @param yId
	 *            youtube id
	 * @param opFile
	 *            destioan address of file to be written i.e
	 *            /sdcard/survey/video file
	 * @return
	 */
	public static final String downloadYoutubeVideo(String yId, String opFile)
	{
		String youtube = "http://www.youtube.com/watch?v=" + yId
				+ "&nomobile=1";

		String result = null;
		int begin, end;
		String tmpstr = null;
		try
		{
			URL url = new URL(youtube);
			// We are retriving youtube html page of that video which contain
			// url of different format of video
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setRequestMethod("GET");
			InputStream stream = con.getInputStream();
			InputStreamReader reader = new InputStreamReader(stream);
			StringBuffer buffer = new StringBuffer();
			char[] buf = new char[262144];
			int chars_read;
			while ((chars_read = reader.read(buf, 0, 262144)) != -1)
			{
				buffer.append(buf, 0, chars_read);
			}
			tmpstr = buffer.toString();

			begin = tmpstr.indexOf("url_encoded_fmt_stream_map=");
			end = tmpstr.indexOf("&", begin + 27);
			if (end == -1)
			{
				end = tmpstr.indexOf("\"", begin + 27);
			}
			tmpstr = Uri.decode(tmpstr.substring(begin + 27, end));

		} catch (MalformedURLException e)
		{
			throw new RuntimeException(Messages.INVALID_VIDEO_URL);
		} catch (IOException e)
		{
			throw new RuntimeException(Messages.CAN_NOT_RESOLVE_THIS_URL);
		}

		Vector<String> url_encoded_fmt_stream_map = new Vector<String>();
		begin = 0;
		end = tmpstr.indexOf(",");

		while (end != -1)
		{

			url_encoded_fmt_stream_map.addElement(tmpstr.substring(begin, end));
			begin = end + 1;
			end = tmpstr.indexOf(",", begin);
		}

		url_encoded_fmt_stream_map.addElement(tmpstr.substring(begin,
				tmpstr.length()));

		Enumeration<String> url_encoded_fmt_stream_map_enum = url_encoded_fmt_stream_map
				.elements();
		// iterate over all urls and checks there types if 18 we found desire
		// url else null
		while (url_encoded_fmt_stream_map_enum.hasMoreElements())
		{
			tmpstr = (String) url_encoded_fmt_stream_map_enum.nextElement();
			begin = tmpstr.indexOf("itag=18");
			if (begin != -1)
			{
				end = tmpstr.indexOf("&", begin + 5);

				if (end == -1)
				{
					end = tmpstr.length();
				}

				int fmt = Integer.parseInt(tmpstr.substring(begin + 5, end));

				if (fmt == 18)
				{
					begin = tmpstr.indexOf("url=");
					if (begin != -1)
					{
						end = tmpstr.indexOf("&", begin + 4);
						if (end == -1)
						{
							end = tmpstr.length();
						}
						result = Uri.decode(tmpstr.substring(begin + 4, end));

						break;
					}
				}
			}
		}
		try
		{
			URL u = new URL(result);
			HttpURLConnection c = (HttpURLConnection) u.openConnection();
			c.setRequestMethod("GET");
			/*
			 * c.setRequestProperty("Youtubedl-no-compression", "True");
			 * c.setRequestProperty("User-Agent", "YouTube");
			 */

			c.setDoOutput(true);
			c.connect();

			FileOutputStream f = new FileOutputStream(new File(opFile));

			InputStream in = c.getInputStream();
			byte[] buffer = new byte[1024];
			int sz = 0;
			while ((sz = in.read(buffer)) > 0)
			{
				f.write(buffer, 0, sz);
			}
			f.close();
		} catch (FileNotFoundException e)
		{
			throw new RuntimeException(Messages.NO_DIR_ERROR);
		} catch (Exception e)
		{
			throw new RuntimeException(Messages.FAIL_TO_DOWNLOAD);
		}
		return opFile;

	}

	/**
	 * 
	 * @param yId
	 * @return
	 */
	public static final String calculateRtspUrl(String yId)
	{

		return null;
	}

	/**
	 * Passing a Standard youtube url
	 * http://www.youtube.com/watch?v=-te7l4MZp5U&feature=g-all-lik will return
	 * youtube id -te7l4MZp5U
	 * 
	 * @param youtubeUrl
	 * @return
	 */
	public static final String getVideoIdFromUrl(String youtubeUrl)
	{
		String video_id = youtubeUrl.split("v=")[1];
		int ampersandPosition = video_id.indexOf('&');
		if (ampersandPosition != -1)
		{
			video_id = video_id.substring(0, ampersandPosition);
		}

		return video_id;

	}

	/**
	 * Calculate the YouTube URL to load the video. Includes retrieving a token
	 * that YouTube requires to play the video.
	 * 
	 * @param pYouTubeFmtQuality
	 *            quality of the video. 17=low, 18=high
	 * @param pYouTubeVideoId
	 *            the id of the video
	 * @return the url string that will retrieve the video
	 * @throws IOException
	 */
	public static String calculateYouTubeUrl(int pYouTubeFmtQuality,
			String pYouTubeVideoId) throws IOException, Exception
	{
		String lUriStr = null;
		HttpClient lClient = new DefaultHttpClient();

		HttpGet lGetMethod = new HttpGet(YOUTUBE_VIDEO_INFORMATION_URL
				+ pYouTubeVideoId);

		HttpResponse lResp = null;

		lResp = lClient.execute(lGetMethod);

		ByteArrayOutputStream lBOS = new ByteArrayOutputStream();
		String lInfoStr = null;

		lResp.getEntity().writeTo(lBOS);
		lInfoStr = new String(lBOS.toString("UTF-8"));

		String[] lArgs = lInfoStr.split("&");
		Map<String, String> lArgMap = new HashMap<String, String>();
		for (int i = 0; i < lArgs.length; i++)
		{
			String[] lArgValStrArr = lArgs[i].split("=");
			if (lArgValStrArr != null)
			{
				if (lArgValStrArr.length >= 2)
				{
					lArgMap.put(lArgValStrArr[0],
							URLDecoder.decode(lArgValStrArr[1]));
				}
			}
		}

		// Find out the URI string from the parameters

		// Populate the list of formats for the video
		String lFmtList = URLDecoder.decode(lArgMap.get("fmt_list"));
		ArrayList<Format> lFormats = new ArrayList<Format>();
		if (null != lFmtList)
		{
			String lFormatStrs[] = lFmtList.split(",");

			for (String lFormatStr : lFormatStrs)
			{
				Format lFormat = new Format(lFormatStr);
				lFormats.add(lFormat);
			}
		}

		// Populate the list of streams for the video
		String lStreamList = lArgMap.get("url_encoded_fmt_stream_map");
		if (null != lStreamList)
		{
			String lStreamStrs[] = lStreamList.split(",");
			ArrayList<VideoStream> lStreams = new ArrayList<VideoStream>();
			for (String lStreamStr : lStreamStrs)
			{
				VideoStream lStream = new VideoStream(lStreamStr);
				lStreams.add(lStream);
			}

			// Search for the given format in the list of video formats
			// if it is there, select the corresponding stream
			// otherwise if fallback is requested, check for next lower format

			Format lSearchFormat = new Format(pYouTubeFmtQuality);
			while (!lFormats.contains(lSearchFormat) && true)
			{
				int lOldId = lSearchFormat.getId();
				int lNewId = getSupportedFallbackId(lOldId);

				if (lOldId == lNewId)
				{
					break;
				}
				lSearchFormat = new Format(lNewId);
			}

			int lIndex = lFormats.indexOf(lSearchFormat);
			if (lIndex >= 0)
			{
				VideoStream lSearchStream = lStreams.get(lIndex);
				lUriStr = lSearchStream.getUrl();
			}

		}
		// Return the URI string. It may be null if the format (or a fallback
		// format if enabled)
		// is not found in the list of formats for the video
		return lUriStr;
	}

	public static int getSupportedFallbackId(int pOldId)
	{
		final int lSupportedFormatIds[] =
		{ 13, // 3GPP (MPEG-4 encoded) Low quality
				17, // 3GPP (MPEG-4 encoded) Medium quality
				18, // MP4 (H.264 encoded) Normal quality
				22, // MP4 (H.264 encoded) High quality
				37 // MP4 (H.264 encoded) High quality
		};
		int lFallbackId = pOldId;
		for (int i = lSupportedFormatIds.length - 1; i >= 0; i--)
		{
			if (pOldId == lSupportedFormatIds[i] && i > 0)
			{
				lFallbackId = lSupportedFormatIds[i - 1];
			}
		}
		return lFallbackId;
	}

}
